﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace KGE.Define
{
    public class NPK
    {
        struct NPK_Header
        {
            public string flag;//= new char[16] { 'N', 'e', 'o', 'p', 'l', 'e', 'P', 'a', 'c', 'k', '_', 'B', 'i', 'l', 'l', 0 };
            public int count;
        }
        struct NPK_Indexer
        {
            public int offset;
            public int size;
            public string name;
        }
        struct NImgF_Header
        {
            public string flag;
            public int index_size;
            public int unknow1;
            public int unknow2;
            public int index_count;
        }
        struct NImgF_Indexer
        {
            /// <summary>
            /// 0xe:1555  0xf:4444    0x10:8888   0x11:link
            /// </summary>
            public int data_type;
            /// <summary>
            /// 0x6:zlib  0x5:non-comp
            /// </summary>
            public int data_format;
            public int width;
            public int height;
            public int size;
            public int x;
            public int y;
            public int max_width;
            public int max_height;
        }
        struct NImageF_Header
        {
            public string flag;
            public int unknow;
            public int index_count;
        }
        struct NImageF_Indexer
        {
            /// <summary>
            /// 0xe:1555  0xf:4444    0x10:8888   0x11:link
            /// </summary>
            public int data_type;
            /// <summary>
            /// 0x6:zlib  0x5:non-comp
            /// </summary>
            public int data_format;
            public int width;
            public int height;
            public int size;
            public int x;
            public int y;
            public int max_width;
            public int max_height;
            //public byte[] data;
        }

        byte[] raw_data;
        //bool is_IMG;

        const string DNF_NPK_KEY = "puchikon@neople dungeon and fighter DNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNFDNF\0";

        public int GetPackageCount()
        {
            return npk_header.count;
        }
        public int GetImageCount(int pak_index)
        {
            int c = 0;
            if (NImgF_I != null)
            {
                c = NImgF_H[pak_index].index_count;
            }
            if (NImageF_I != null)
            {
                c = NImageF_H[pak_index].index_count;
            }
            return c;
        }

        public string[] PackageNames
        {
            get
            {
                string[] strs = new string[npk_header.count];
                for (int i = 0; i < strs.Length; i++)
                {
                    string str = npk_indexer[i].name;

                    strs[i] = str.Substring(0, str.LastIndexOf(".")); ;
                }
                return strs;
            }
        }

        string decode(byte[] data)
        {
            byte[] BYTE = Encoding.ASCII.GetBytes(DNF_NPK_KEY);

            byte[] r = new byte[256];
            for (int i = 0; i < r.Length; i++)
            {
                r[i] = (byte)(data[i] ^ BYTE[i]);
            }
            string str = Encoding.ASCII.GetString(r);
            int p = str.IndexOf("\0");
            return str.Substring(0, p);
        }

        NPK_Header npk_header;
        NPK_Indexer[] npk_indexer;

        NImgF_Header[] NImgF_H;
        NImageF_Header[] NImageF_H;

        NImgF_Indexer[][] NImgF_I;
        NImageF_Indexer[][] NImageF_I;

        public NPK(byte[] data)
        {
            raw_data = data;
            analyze();
        }

        public Image this[int pak_index, int img_index]
        {
            get
            {
                return GetImage(pak_index, img_index);
            }
        }

        public Size GetImageSize(int pak_index, int img_index)
        {
            Size s = new Size();

            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {

                    s = new Size(NImgF_I[pak_index][img_index].width, NImgF_I[pak_index][img_index].height);
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index] != null)
                {
                    s = new Size(NImageF_I[pak_index][img_index].width, NImageF_I[pak_index][img_index].height);
                }
            }
            return s;
        }
        public Size GetPackageSize(int pak_index, int img_index)
        {
            Size s = new Size();
            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {
                    s = new Size(NImgF_I[pak_index][img_index].max_width, NImgF_I[pak_index][img_index].max_height);
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index] != null)
                {
                    s = new Size(NImageF_I[pak_index][img_index].max_width, NImageF_I[pak_index][img_index].max_height);
                }
            }
            return s;
        }
        public Point GetImageLocation(int pak_index, int img_index)
        {
            Point p = new Point();
            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {
                    p = new Point(NImgF_I[pak_index][img_index].x, NImgF_I[pak_index][img_index].y);
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index] != null)
                {
                    p = new Point(NImageF_I[pak_index][img_index].x, NImageF_I[pak_index][img_index].y);
                }
            }
            return p;
        }
        public string GetDataFormat(int pak_index, int img_index)
        {
            string str = "";
            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {

                    if (NImgF_I[pak_index][img_index].data_format == 0x6)
                    {
                        str = "zlib";
                    }
                    else if (NImgF_I[pak_index][img_index].data_format == 0x5)
                    {
                        str = "raw";
                    }
                    else
                    {
                        str = "LINK TO " + NImgF_I[pak_index][img_index].data_format;
                    }
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index] != null)
                {
                    if (NImageF_I[pak_index][img_index].data_format == 0x6)
                    {
                        str = "zlib";
                    }
                    else if (NImageF_I[pak_index][img_index].data_format == 0x5)
                    {
                        str = "raw";
                    }
                    else
                    {
                        str = "LINK TO " + NImageF_I[pak_index][img_index].data_format;
                    }
                }
            }
            return str;
        }
        public string GetDataType(int pak_index, int img_index)
        {
            string str = "";
            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {
                    if (NImgF_I[pak_index][img_index].data_type == 0xe)
                    {
                        str = "ARGB1555";
                    }
                    else if (NImgF_I[pak_index][img_index].data_type == 0xf)
                    {
                        str = "ARGB4444";
                    }
                    else if (NImgF_I[pak_index][img_index].data_type == 0x10)
                    {
                        str = "ARGB8888";
                    }
                    else if (NImgF_I[pak_index][img_index].data_type == 0x11)
                    {
                        str = "LINK";
                    }
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index] != null)
                {
                    if (NImageF_I[pak_index][img_index].data_type == 0xe)
                    {
                        str = "ARGB1555";
                    }
                    else if (NImageF_I[pak_index][img_index].data_type == 0xf)
                    {
                        str = "ARGB4444";
                    }
                    else if (NImageF_I[pak_index][img_index].data_type == 0x10)
                    {
                        str = "ARGB8888";
                    }
                    else if (NImageF_I[pak_index][img_index].data_type == 0x11)
                    {
                        str = "LINK";
                    }
                }
            }
            return str;
        }


        string ReadString(MemoryStream ms, int len)
        {
            return Encoding.ASCII.GetString(ReadByte(ms, len));
        }
        byte[] ReadByte(MemoryStream ms, int len)
        {
            byte[] buf = new byte[len];
            ms.Read(buf, 0, len);
            return buf;
        }
        int ReadInt(MemoryStream ms)
        {
            int v = 0;
            byte[] d = new byte[4];

            for (int i = 0; i < d.Length; i++)
            {
                //ms.Read(d, (int)ms.Position, 4);
                d[i] = (byte)ms.ReadByte();
                v |= d[i] << i * 8;
            }
            return v;
        }
        void analyze()
        {
            MemoryStream ms = new MemoryStream(raw_data);

            #region read npk header
            npk_header.flag = ReadString(ms, 16);
            npk_header.count = ReadInt(ms);
            #endregion
            #region read npk indexer(s)
            npk_indexer = new NPK_Indexer[npk_header.count];
            for (int i = 0; i < npk_header.count; i++)
            {
                npk_indexer[i] = new NPK_Indexer();
                npk_indexer[i].offset = ReadInt(ms);
                npk_indexer[i].size = ReadInt(ms);
                npk_indexer[i].name = decode(ReadByte(ms, 256));

                npk_indexer[i].name = npk_indexer[i].name.Substring(1 + npk_indexer[i].name.LastIndexOf("/"));
            }
            #endregion

            NImgF_H = new NImgF_Header[npk_indexer.Length];
            NImgF_I = new NImgF_Indexer[npk_indexer.Length][];

            NImageF_H = new NImageF_Header[npk_indexer.Length];
            NImageF_I = new NImageF_Indexer[npk_indexer.Length][];

            for (int i = 0; i < npk_header.count; i++)
            {
                ms.Position = npk_indexer[i].offset;

                string str = ReadString(ms, 10);
                if (str == "Neople Img")
                {
                    NImageF_H = null;
                    NImageF_I = null;
                    #region read img header
                    ms.Position -= 10;

                    NImgF_Header ih = new NImgF_Header();
                    ih.flag = ReadString(ms, 16);
                    ih.index_size = ReadInt(ms);
                    ih.unknow1 = ReadInt(ms);
                    ih.unknow2 = ReadInt(ms);
                    ih.index_count = ReadInt(ms);
                    NImgF_H[i] = ih;
                    #endregion
                    NImgF_I[i] = new NImgF_Indexer[NImgF_H[i].index_count];
                    #region read img indexer
                    for (int j = 0; j < ih.index_count; j++)
                    {
                        NImgF_Indexer ii = new NImgF_Indexer();
                        ii.data_type = ReadInt(ms);
                        ii.data_format = ReadInt(ms);
                        if (ii.data_type != 0x11)
                        {
                            ii.width = ReadInt(ms);
                            ii.height = ReadInt(ms);
                            ii.size = ReadInt(ms);
                            ii.x = ReadInt(ms);
                            ii.y = ReadInt(ms);
                            ii.max_width = ReadInt(ms);
                            ii.max_height = ReadInt(ms);
                            if (ii.data_format == 5)
                            {
                                if (ii.data_type == 0xe || ii.data_type == 0xf)
                                {
                                    ii.size = ii.width * ii.height * 2;
                                }
                                else if (ii.data_type == 0x10)
                                {
                                    ii.size = ii.width * ii.height * 4;
                                }
                            }
                            NImgF_I[i][j] = ii;
                        }
                        else
                        {
                            NImgF_I[i][j] = NImgF_I[i][ii.data_format];
                            NImgF_I[i][j].data_type = ii.data_type;
                            NImgF_I[i][j].data_format = ii.data_format;
                            NImgF_I[i][j].size = 0;
                        }

                    }
                    #endregion
                }
                else if (str == "Neople Ima")
                {
                    NImgF_H = null;
                    NImgF_I = null;

                    #region read image header
                    ms.Position -= 10;

                    NImageF_Header ih = new NImageF_Header();
                    ih.flag = ReadString(ms, 24);
                    ih.unknow = ReadInt(ms);
                    ih.index_count = ReadInt(ms);
                    NImageF_H[i] = ih;
                    #endregion
                    NImageF_I[i] = new NImageF_Indexer[NImageF_H[i].index_count];
                    #region read image indexer
                    for (int j = 0; j < ih.index_count; j++)
                    {
                        NImageF_Indexer ii = new NImageF_Indexer();
                        ii.data_type = ReadInt(ms);
                        ii.data_format = ReadInt(ms);
                        if (ii.data_type != 0x11)
                        {
                            ii.width = ReadInt(ms);
                            ii.height = ReadInt(ms);
                            ii.size = ReadInt(ms);
                            ii.x = ReadInt(ms);
                            ii.y = ReadInt(ms);
                            ii.max_width = ReadInt(ms);
                            ii.max_height = ReadInt(ms);
                            ms.Position += ii.size;
                            if (ii.data_format == 5)
                            {
                                if (ii.data_type == 0xe || ii.data_type == 0xf)
                                {
                                    ii.size = ii.width * ii.height * 2;
                                }
                                else if (ii.data_type == 0x10)
                                {
                                    ii.size = ii.width * ii.height * 4;
                                }
                            }
                            NImageF_I[i][j] = ii;
                        }
                        else
                        {
                            NImageF_I[i][j] = NImageF_I[i][ii.data_format];
                            NImageF_I[i][j].data_type = ii.data_type;
                            NImageF_I[i][j].data_format = ii.data_format;
                            NImageF_I[i][j].size = 0;
                        }
                    }
                    #endregion
                }
            }
        }
        public Image GetImage(int pak_index, int img_index)
        {
            MemoryStream ms = new MemoryStream(raw_data);

            if (NImgF_I != null)
            {
                if (NImgF_I[pak_index] != null)
                {
                    if (NImgF_I[pak_index][img_index].data_type == 0x11)
                    {
                        img_index = NImgF_I[pak_index][img_index].data_format;
                    }
                    else
                    {
                        if (NImgF_I[pak_index][img_index].width == 0 || NImgF_I[pak_index][img_index].height == 0)
                        {
                            return null;
                        }
                    }

                    Bitmap bm = new Bitmap(NImgF_I[pak_index][img_index].width, NImgF_I[pak_index][img_index].height);

                    ms.Position = npk_indexer[pak_index].offset;
                    ms.Position += 32;
                    ms.Position += NImgF_H[pak_index].index_size;

                    for (int i = 0; i < img_index; i++)
                    {
                        ms.Position += NImgF_I[pak_index][i].size;
                    }

                    //int offset = NImgF_H[pak_index].index_count * NImgF_H[pak_index].index_size;

                    int s = NImgF_I[pak_index][img_index].size;
                    byte[] buff = new byte[s];
                    ms.Read(buff, 0, s);

                    if (NImgF_I[pak_index][img_index].data_format == 5)
                    {
                        byte[] zlib = buff;

                        int pi = 0;

                        for (int y = 0; y < NImgF_I[pak_index][img_index].height; y++)
                        {
                            for (int x = 0; x < NImgF_I[pak_index][img_index].width; x++)
                            {
                                int col = 0;

                                if (NImgF_I[pak_index][img_index].data_type == 0xe)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col = Util.ARGB1555TOARGB8888(col);
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0xf)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col = Util.ARGB4444TOARGB8888(col);
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0x10)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col |= zlib[pi++] << 16;
                                    col |= zlib[pi++] << 24;
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0x11)
                                {
                                }
                                bm.SetPixel(x, y, Color.FromArgb(col));
                            }
                        }
                    }
                    else if (NImgF_I[pak_index][img_index].data_format == 6)
                    {
                        byte[] zlib = Util.Decompress(buff);

                        int pi = 0;

                        for (int y = 0; y < NImgF_I[pak_index][img_index].height; y++)
                        {
                            for (int x = 0; x < NImgF_I[pak_index][img_index].width; x++)
                            {
                                int col = 0;

                                if (NImgF_I[pak_index][img_index].data_type == 0xe)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col = Util.ARGB1555TOARGB8888(col);
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0xf)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col = Util.ARGB4444TOARGB8888(col);
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0x10)
                                {
                                    col = zlib[pi++];
                                    col |= zlib[pi++] << 8;
                                    col |= zlib[pi++] << 16;
                                    col |= zlib[pi++] << 24;
                                }
                                else if (NImgF_I[pak_index][img_index].data_type == 0x11)
                                {
                                }
                                bm.SetPixel(x, y, Color.FromArgb(col));
                            }
                        }

                    }
                    return bm;
                }
            }
            if (NImageF_I != null)
            {
                if (NImageF_I[pak_index][img_index].data_type == 0x11)
                {
                    img_index = NImageF_I[pak_index][img_index].data_format;
                }
                else
                {
                    if (NImageF_I[pak_index][img_index].width == 0 || NImageF_I[pak_index][img_index].height == 0)
                    {
                        return null;
                    }
                }
                Bitmap bm = new Bitmap(NImageF_I[pak_index][img_index].width, NImageF_I[pak_index][img_index].height);

                ms.Position = npk_indexer[pak_index].offset;

                ms.Position += 32;

                if (NImageF_I[pak_index][img_index].data_type == 0x11)
                {
                    ms.Position += 8;
                }
                else
                {
                    ms.Position += 36;
                }

                for (int i = 0; i < img_index; i++)
                {
                    ms.Position += NImageF_I[pak_index][i].size;

                    if (NImageF_I[pak_index][i].data_type == 0x11)
                    {
                        ms.Position += 8;
                    }
                    else
                    {
                        ms.Position += 36;
                    }
                }

                int s = NImageF_I[pak_index][img_index].size;
                byte[] buff = new byte[s];
                ms.Read(buff, 0, s);

                if (NImageF_I[pak_index][img_index].data_format == 5)
                {
                }
                else if (NImageF_I[pak_index][img_index].data_format == 6)
                {
                    byte[] zlib = Util.Decompress(buff);

                    int pi = 0;

                    for (int y = 0; y < NImageF_I[pak_index][img_index].height; y++)
                    {
                        for (int x = 0; x < NImageF_I[pak_index][img_index].width; x++)
                        {
                            int col = 0;

                            if (NImageF_I[pak_index][img_index].data_type == 0xe)
                            {
                                col = zlib[pi++];
                                col |= zlib[pi++] << 8;
                                col = Util.ARGB1555TOARGB8888(col);
                            }
                            else if (NImageF_I[pak_index][img_index].data_type == 0xf)
                            {
                                col = zlib[pi++];
                                col |= zlib[pi++] << 8;
                                col = Util.ARGB4444TOARGB8888(col);
                            }
                            else if (NImageF_I[pak_index][img_index].data_type == 0x10)
                            {
                                col = zlib[pi++];
                                col |= zlib[pi++] << 8;
                                col |= zlib[pi++] << 16;
                                col |= zlib[pi++] << 24;
                            }
                            else if (NImageF_I[pak_index][img_index].data_type == 0x11)
                            {
                            }
                            bm.SetPixel(x, y, Color.FromArgb(col));
                        }
                    }
                }
                return bm;
            }
            return null;
        }

        public KImagePackage[] ToKImagePackages()
        {
            KImagePackage[] kips = new KImagePackage[npk_header.count];
            for (int i = 0; i < npk_header.count; i++)
            {
                kips[i] = ToKImagePackage(i);
            }
            return kips;
        }
        public KImagePackage ToKImagePackage(int pak_index)
        {
            KImagePackage kip = new KImagePackage();
            kip.Name = PackageNames[pak_index];

            for (int i = 0; i < GetImageCount(pak_index); i++)
            {
                kip.Add(new KImage(GetImage(pak_index, i), i.ToString("d3"), GetImageLocation(pak_index, i)));
            }

            return kip;
        }
    }
}
